package com.gulimall.search.service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.gulimall.common.utils.R;
import com.gulimall.common.vo.SkuEsModel;
import com.gulimall.search.feigh.SkuInfoFeignService;
import com.gulimall.search.vo.SearchParam;
import com.gulimall.search.vo.SearchResult;
import com.gulimall.search.vo.SkuInfo;
import org.apache.commons.lang.StringUtils;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.NestedQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.RangeQueryBuilder;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.nested.NestedAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.nested.ParsedNested;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedLongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

/**
 * @author coder_ccy
 * @date 2022/3/28 14:12
 */

@Service
public class SearchService {

    @Resource
    private RestHighLevelClient client;


    @Resource
    private SkuInfoFeignService skuInfoFeignService;

    public SearchResult search(SearchParam param) throws IOException {

        //1. 构建searchRequest

        SearchRequest searchRequest =buildSearchRequest(param);


        //2. 执行查询
        SearchResponse res = client.search(searchRequest, RequestOptions.DEFAULT);

        //3. 解析结果

        SearchResult  result = analyzingResponse(param,res);

        return result;


    }


    private SearchResult analyzingResponse(SearchParam param,SearchResponse res) {

        SearchResult result = new SearchResult();

        SearchHits hits = res.getHits();


        //分页相关信息
        result.setTotal(
                hits.getTotalHits().value   //总数据
        );
        result.setPageNum(param.getPageNum());
        Long totalPage =  (result.getTotal()%param.getPageSize()>0)?(result.getTotal()/param.getPageSize()+1):(result.getTotal()/param.getPageSize());

        result.setTotalPages(totalPage.intValue());
        result.setPageNum(totalPage.intValue());

        SearchHit[] searchHits = hits.getHits();

        List<SkuEsModel> skuEsModels = new ArrayList<>();
        for (SearchHit searchHit : searchHits) {


            String sourceAsString = searchHit.getSourceAsString();
            SkuEsModel skuEsModel = JSON.parseObject(sourceAsString, SkuEsModel.class);


            // 处理高亮
            Map<String, HighlightField> highlightFields = searchHit.getHighlightFields();
            HighlightField skuTitleHighlight = highlightFields.get("skuTitle");
            if(skuTitleHighlight!=null){
                skuEsModel.setSkuTitle(skuTitleHighlight.getFragments()[0].toString());
                skuEsModels.add(skuEsModel);
            }


        }
        result.setProducts(skuEsModels);


        //处理聚合数据
        Aggregations aggregations = res.getAggregations();
        //brandagg   brandvos
        ArrayList<SearchResult.BrandVo> brandVos = new ArrayList<>();
        ParsedLongTerms brandAgg = aggregations.get("brandAgg");
        List<? extends Terms.Bucket> buckets = brandAgg.getBuckets();
        for (Terms.Bucket bucket : buckets) {
            SearchResult.BrandVo brandVo = new SearchResult.BrandVo();
            brandVo.setBrandId((Long) bucket.getKey());

            Aggregations aggregations1 = bucket.getAggregations();
            ParsedStringTerms brandNameAgg = aggregations1.get("brandNameAgg");
            for (Terms.Bucket brandNameAggBucket : brandNameAgg.getBuckets()) {
                brandVo.setBrandName(brandNameAggBucket.getKeyAsString());
            }

            ParsedStringTerms brandImgAgg = aggregations1.get("brandImgAgg");
            for (Terms.Bucket brandImgAggBucket : brandImgAgg.getBuckets()) {
                brandVo.setBrandImg(brandImgAggBucket.getKeyAsString());
            }
            brandVos.add(brandVo);

        }


        result.setBrands(brandVos);
        //catalogagg  catalogvos

        ArrayList<SearchResult.CatalogVo> catalogVos = new ArrayList<>();
        ParsedLongTerms catalogAgg = res.getAggregations().get("catalogAgg");
        for (Terms.Bucket catalogAggBucket : catalogAgg.getBuckets()) {

            SearchResult.CatalogVo catalogVo = new SearchResult.CatalogVo();
            catalogVo.setCatalogId(catalogAggBucket.getKeyAsNumber().longValue());


            ParsedStringTerms catalogNameAgg = catalogAggBucket.getAggregations().get("catalogNameAgg");
            for (Terms.Bucket bucket : catalogNameAgg.getBuckets()) {
                catalogVo.setCatalogName( bucket.getKeyAsString());
            }

            catalogVos.add(catalogVo);
        }

        catalogVos.add(new SearchResult.CatalogVo(2L,"电视"));


        result.setCatalogs(catalogVos);


        //attrs
        ParsedNested attrs = res.getAggregations().get("attrsAgg");
        ParsedLongTerms attrIdAgg = attrs.getAggregations().get("attrIdAgg");


        ArrayList<SearchResult.AttrVo> attrVos = new ArrayList<>();
        for (Terms.Bucket bucket : attrIdAgg.getBuckets()) {

            SearchResult.AttrVo attrVo = new SearchResult.AttrVo();
            attrVo.setAttrId(bucket.getKeyAsNumber().longValue());

            ParsedStringTerms attrNameAgg = bucket.getAggregations().get("attrNameAgg");
            attrVo.setAttrName(attrNameAgg.getBuckets().get(0).getKeyAsString());


            ParsedStringTerms attrValueAgg = bucket.getAggregations().get("attrValueAgg");
            attrVo.setAttrValue(Arrays.asList(attrValueAgg.getBuckets().get(0).getKeyAsString()));

            attrVos.add(attrVo);

        }
        result.setAttrs(attrVos);

        return result;



    }

    private SearchRequest buildSearchRequest(SearchParam searchParam) {

        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();


        //1. TODO queryBuilder
        BoolQueryBuilder que = new BoolQueryBuilder();

        //1.1 must
        if(searchParam!=null && StringUtils.isNotEmpty(searchParam.getKeyword()))
        que.must(QueryBuilders.matchQuery("skuTitle",searchParam.getKeyword()));

        //1.2 filter
        //1.2.1
        que.filter(QueryBuilders.termQuery("hasStock",true));

        //1.2.2

        RangeQueryBuilder rangeQueryBuilder = new RangeQueryBuilder("skuPrice");
        rangeQueryBuilder.gte(1000);
        rangeQueryBuilder.lte(20000);
        que.filter(rangeQueryBuilder);

        //1.2.3

        if(searchParam.getBrandId()!=null && searchParam.getBrandId().size()>0)
        que.filter(QueryBuilders.termsQuery("brandId",searchParam.getBrandId()));


        //1.2.4
        if(searchParam.getCatalog3Id()!=null)
        que.filter(QueryBuilders.termQuery("catalogId",searchParam.getCatalog3Id()));

        //1.2.5

        BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();

        List<Long> longs1 = Arrays.asList(1L, 2L, 3L);

        boolQueryBuilder.must(QueryBuilders.termsQuery("attrs.attrId",longs1));

        NestedQueryBuilder nestedQueryBuilder = new NestedQueryBuilder("attrs",boolQueryBuilder, ScoreMode.None);
        que.filter(nestedQueryBuilder);

        //5. TODO heghlightBuilder
        HighlightBuilder heghlightBuilder  = new HighlightBuilder();
        heghlightBuilder.field("skuTitle");
        heghlightBuilder.preTags("<span style='color:skyblue'>");
        heghlightBuilder.postTags("</span>");


        //6. TODO aggs

        //6.1 brandAgg
        TermsAggregationBuilder brandAgg = AggregationBuilders.terms("brandAgg").field("brandId");
        TermsAggregationBuilder brandNameAgg = AggregationBuilders.terms("brandNameAgg").field("brandName");
        TermsAggregationBuilder brandImgAgg = AggregationBuilders.terms("brandImgAgg").field("brandImg");
        brandAgg.subAggregation(brandNameAgg);
        brandAgg.subAggregation(brandImgAgg);

        //6.2 catelogAgg
        TermsAggregationBuilder catelogAgg = AggregationBuilders.terms("catalogAgg").field("catalogId");
        TermsAggregationBuilder catelogNameAgg = AggregationBuilders.terms("catalogNameAgg").field("catalogName");
        catelogAgg.subAggregation(catelogNameAgg);

        //6.3 attrsAgg
        NestedAggregationBuilder attrsAgg = new NestedAggregationBuilder("attrsAgg", "attrs");

        TermsAggregationBuilder attrIdAgg = AggregationBuilders.terms("attrIdAgg").field("attrs.attrId");
        TermsAggregationBuilder attrNameAgg = AggregationBuilders.terms("attrNameAgg").field("attrs.attrName");
        TermsAggregationBuilder attrValueAgg = AggregationBuilders.terms("attrValueAgg").field("attrs.attrValue");

        attrsAgg.subAggregation(attrIdAgg);
        attrIdAgg.subAggregation(attrNameAgg);
        attrIdAgg.subAggregation(attrValueAgg);


        //1. query
        searchSourceBuilder.query(que);

        //2. sort
        searchSourceBuilder.sort("saleCount", SortOrder.DESC);

        //3. from
        searchSourceBuilder.from(0);

        //4. size
        searchSourceBuilder.size(10);

        //5. highlight
        searchSourceBuilder.highlighter(heghlightBuilder);

        //6. aggs
        searchSourceBuilder.aggregation(brandAgg);
        searchSourceBuilder.aggregation(catelogAgg);
        searchSourceBuilder.aggregation(attrsAgg);




        SearchRequest searchRequest = new SearchRequest();
        searchRequest.indices("product");
        searchRequest.source(searchSourceBuilder);

        return searchRequest;






    }

    public SkuInfo getSkuInfo(long skuId) {


        R r = skuInfoFeignService.info(skuId);
        SkuInfo sku = r.getData("skuInfo",new TypeReference<SkuInfo>() {
        });

        return sku;

    }
}
